﻿using Gif.Components;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

/// <summary>
/// 劳承杰 https://blog.csdn.net/qq_35955916
/// </summary>
namespace MadeGIF.BLL
{
    /// <summary>
    /// 屏幕录制GIF小工具
    /// </summary>
    public class Made
    {
        bool _isMousePress = false;

        /// <summary>
        /// 鼠标开始位置
        /// </summary>
        Point _beginPoint = new Point(0, 0);
        /// <summary>
        /// 鼠标结束位置
        /// </summary>
        Point _endPoint = new Point(0, 0);
        /// <summary>
        /// 截屏区域左上角位置
        /// </summary>
        Point _startPoint
        {
            //  2   |   1
            //      |
            //------0------
            //      |
            //  3   |   4
            get
            {
                var x = _endPoint.X - _beginPoint.X;
                var y = _endPoint.Y - _beginPoint.Y;
                //鼠标结束位置在第4象限
                if (x > 0 && y > 0) return _beginPoint;
                //鼠标结束位置在第2象限
                if (x < 0 && y < 0) return _endPoint;
                //鼠标结束位置在第3象限
                if (x < 0 && y > 0) return new Point(_endPoint.X, _beginPoint.Y);
                //鼠标结束位置在第1象限
                if (x > 0 && y < 0) return new Point(_beginPoint.X, _endPoint.Y);
                //鼠标结束位置在坐标轴上
                return _beginPoint;
            }
        }
        /// <summary>
        /// 截屏区域宽
        /// </summary>
        int _width => Math.Abs(_endPoint.X - _beginPoint.X);
        /// <summary>
        /// 截屏区域高
        /// </summary>
        int _height => Math.Abs(_endPoint.Y - _beginPoint.Y);
        /// <summary>
        /// 帧率（帧/秒）
        /// </summary>
        int _fps => Properties.Settings1.Default.FPS;
        /// <summary>
        /// 画质 1~100 数值越低，画质越差，生成速度越快
        /// </summary>
        double _quality => Properties.Settings1.Default.Quality;
        /// <summary>
        /// gif是否重复播放
        /// </summary>
        bool _isRepeat => Properties.Settings1.Default.Repeat;
        /// <summary>
        /// gif文件保存位置
        /// </summary>
        string _savePath => Properties.Settings1.Default.SavePath;
        /// <summary>
        /// 水印文字
        /// </summary>
        string _textMark => Properties.Settings1.Default.TextMark;
        /// <summary>
        /// 水印文字颜色名
        /// </summary>
        string _textMarkColor => Properties.Settings1.Default.TextMarkColor;
        /// <summary>
        /// 截取到的帧
        /// </summary>
        List<Image> _tempFrames = new List<Image>();

        /// <summary>
        /// 是否为截屏模式 true-截图模式 false-录制gif模式
        /// </summary>
        bool isScreenShot = false;
        /// <summary>
        /// 是否正在录制gif
        /// </summary>
        bool isProcessing = false;
        /// <summary>
        /// 停止录屏控制
        /// </summary>
        CancellationTokenSource _screenCaptureCTS = null;

        /// <summary>
        /// 通知事件
        /// </summary>
        event Action<int,string> _onMessageEvent;
        public Made()
        {
            ToolBar toolBar = new ToolBar();
            _onMessageEvent += toolBar.OnMessage;
            toolBar.CheckedChanged += (e) => isScreenShot = e;
            toolBar.RectangleSelectEvent += (e, a) => {
                if (isProcessing) return;
                MaskLayer mask = new MaskLayer();
                mask.Sketchpad.MouseDown += Mask_MouseDown;
                mask.Sketchpad.MouseUp += Mask_MouseUp;
                mask.Sketchpad.MouseMove += Mask_MouseMove;
                mask.ShowDialog();
            };
            toolBar.StartEvent += (e, a) => {
                if (isScreenShot || isProcessing) return;
                if(_width!=0 && _height != 0)
                {
                    isProcessing = true;
                    _screenCaptureCTS = new CancellationTokenSource();
                    Task.Factory.StartNew((ct) => ScreenCapture((CancellationToken)ct), _screenCaptureCTS.Token);
                    Task.Factory.StartNew((ct) =>
                    {
                        Stopwatch stopwatch = new Stopwatch();
                        stopwatch.Start();
                        while (!((CancellationToken)ct).IsCancellationRequested)
                        {
                            _onMessageEvent?.Invoke(2, $"{stopwatch.Elapsed.Hours.ToString("00")}:{stopwatch.Elapsed.Minutes.ToString("00")}:{stopwatch.Elapsed.Seconds.ToString("00")}.{stopwatch.Elapsed.Milliseconds.ToString("000")}");
                            Thread.Sleep(100);
                        }
                        stopwatch.Stop();

                    }, _screenCaptureCTS.Token);
                }
                else
                {
                    MessageBox.Show("截屏区域错误，请重新选择区域");
                }    
            };
            toolBar.StopEvent += (e, a) => {
                _screenCaptureCTS?.Cancel();
            };
            toolBar.SettingEvent += (e, a) => {
                if (isProcessing) return;
                Setting setting = new Setting();
                setting.SettingSaveEvent = (arg) => {
                    Properties.Settings1.Default.FPS = Convert.ToInt32(arg.FPS);
                    Properties.Settings1.Default.Quality = Convert.ToDouble(arg.Quality);
                    Properties.Settings1.Default.SavePath = Convert.ToString(arg.SavePath);
                    Properties.Settings1.Default.Repeat = Convert.ToBoolean(arg.Repeat);
                    Properties.Settings1.Default.TextMark = Convert.ToString(arg.TextMark);
                    Properties.Settings1.Default.TextMarkColor = Convert.ToString(arg.TextMarkColor);

                    Properties.Settings1.Default.Save();
                    MessageBox.Show("保存成功");
                };
                setting.ShowDialog();
            };
            toolBar.ShowDialog();
        }
        #region MaskLayer 区域选取
        Image _sketchpad = null;
        Graphics _sketchpadGraphics = null;
        void Mask_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                if (_isMousePress)
                {
                    _isMousePress = false;
                    _sketchpad?.Dispose();
                    _sketchpadGraphics?.Dispose();
                    _onMessageEvent?.Invoke(1,$"{_width}*{_height}");
                    ((Form)((PictureBox)sender).Parent).Close();
                    #region 截图模式
                    if (isScreenShot && _width != 0 && _height != 0)
                    {
                        Task.Factory.StartNew((syncContext) =>
                        {
                            Thread.Sleep(100);
                            ScreenCapture(new CancellationToken(true));
                            ((SynchronizationContext)syncContext).Post(p =>
                            {
                                //回到ui线程
                                Clipboard.SetImage(_tempFrames[0]);
                            }, null);
                            MessageBox.Show("截图成功,已经放到了系统粘贴板");
                            _tempFrames.Clear();
                        }, SynchronizationContext.Current); ;
                    }
                    #endregion
                }
            }
        }

        void Mask_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                _sketchpad = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
                _sketchpadGraphics = Graphics.FromImage(_sketchpad);

                _beginPoint = e.Location;
                _isMousePress = true;
            }
            if (e.Button == MouseButtons.Right)
            {
                if (!_isMousePress)
                    ((Form)((PictureBox)sender).Parent).Close();
                else
                    _isMousePress = false;
            }
        }

        void Mask_MouseMove(object sender, MouseEventArgs e)
        {
            if (_isMousePress && _sketchpadGraphics != null)
            {
                _endPoint = e.Location;

                _sketchpadGraphics.Clear(Color.FromArgb(0, 0, 0, 0));
                _sketchpadGraphics.DrawRectangle(new Pen(Brushes.Red, 2), _startPoint.X, _startPoint.Y, _width, _height);
                _sketchpadGraphics.FillRectangle(Brushes.White, _startPoint.X + 1, _startPoint.Y + 1, _width - 2, _height - 2);
                var temp = ((PictureBox)sender);
                temp.Image = _sketchpad;
                temp.Refresh();
            }
        }
        #endregion


        /// <summary>
        /// 截屏
        /// </summary>
        /// <param name="ct"></param>
        void ScreenCapture(CancellationToken ct)
        {
            _onMessageEvent?.Invoke(0, isScreenShot ? "ToolBar" : "录制中...");
            //按照选定的区域宽高创建位图
            Image farme = new Bitmap(_width, _height);
            //从一个继承自Image类的对象中创建Graphics对象
            Graphics graphics = Graphics.FromImage(farme);
            //在farme里的绘制起点
            var point = new Point(0, 0);
            //在farme里的绘制大小
            var size = new Size(_width, _height);
            //水印字体
            var font = new Font("微软雅黑", 12);
            int i = 0;
            _tempFrames.Clear();
            do
            {
                //抓屏
                graphics.Clear(Color.FromArgb(0, 0, 0, 0));
                graphics.CopyFromScreen(_startPoint, point, size);
                if (_textMark.Trim() != string.Empty) graphics.DrawString(_textMark, font, new SolidBrush(Color.FromName(_textMarkColor)), 0, _height - 24);
                _tempFrames.Add((Image)farme.Clone());
                i++;
                Thread.Sleep(1000 / _fps);
            } while (!ct.IsCancellationRequested);
            farme.Dispose();
            graphics.Dispose();
            Debug.WriteLine($"截屏完成,共{i}帧...");
            if (_quality != 100) ImageCompression();
            if (!isScreenShot) ToGif();  
        }
        void ImageCompression()
        {
            int w = Convert.ToInt32(_width * (_quality / 100));
            int h = Convert.ToInt32(_height * (_quality / 100));
            //Image img = new Bitmap(w, h);
            //Graphics graphics = Graphics.FromImage(img);
            //for (int i = 0, len = _tempFrames.Count; i < len; i++)
            //{
            //    var frame = _tempFrames[i];
            //    graphics.Clear(Color.FromArgb(0, 0, 0, 0));
            //    graphics.DrawImage(frame, 0, 0, w, h);
            //    _tempFrames[i] = (Image)img.Clone();
            //}
            //img.Dispose();
            //graphics.Dispose();
            Parallel.For(0, _tempFrames.Count, (i) =>
            {
                Image img0 = new Bitmap(w, h);
                Graphics graphics0 = Graphics.FromImage(img0);
                var frame = _tempFrames[i];
                graphics0.Clear(Color.FromArgb(0, 0, 0, 0));
                graphics0.DrawImage(frame, 0, 0, w, h);
                _tempFrames[i] = (Image)img0.Clone();
                img0.Dispose();
                graphics0.Dispose();
            });
            Debug.WriteLine("图片压缩完成...");
        }
        void ToGif()
        {
            _onMessageEvent?.Invoke(0,$"正在生成GIF...");
            AnimatedGifEncoder gif = new AnimatedGifEncoder();
            //-1：不重复，0：重复
            gif.SetRepeat(true ? 0 : -1);
            gif.SetDelay(1000 / _fps);

            if (string.IsNullOrWhiteSpace(_savePath)) {
                Properties.Settings1.Default.SavePath = $"{AppDomain.CurrentDomain.BaseDirectory}file-gif";
                Properties.Settings1.Default.Save();
            }
            var file = $@"{_savePath}\{DateTime.Now.ToString("yyyyMMddHHmmss")}.gif";
            if (!Directory.Exists(_savePath)) Directory.CreateDirectory(_savePath);

            gif.Start(file);
            Stopwatch sw = new Stopwatch();
            sw.Start();
            var isFirst = true;
            var 预计总耗时 = "";
            var count = _tempFrames.Count;
            foreach (var frame in _tempFrames)
            {
                gif.AddFrame(frame);
                frame.Dispose();
                if (isFirst)
                {
                    预计总耗时 = $"预计耗时：{new DateTime(((sw.ElapsedMilliseconds * count) / 1000) * 10000000).ToString("HH:mm:ss")}";
                    //_onMessageEvent?.Invoke(0,预计总耗时);
                    isFirst = false;
                }
                else
                {
                    _onMessageEvent?.Invoke(0,$"正在生成GIF，{预计总耗时} 实际耗时：{new DateTime((sw.ElapsedMilliseconds / 1000) * 10000000).ToString("HH:mm:ss")}");
                }
                count--;
            }
            gif.Finish();
            sw.Stop();
            _tempFrames.Clear();
            _onMessageEvent?.Invoke(0,$"完成 {$"{预计总耗时} 实际耗时：{new DateTime((sw.ElapsedMilliseconds / 1000) * 10000000).ToString("HH:mm:ss")}"}");
            isProcessing = false;
            Result result = new Result(file);
            result.ShowDialog();
        }
    }
}
